﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml.Linq;
using System.Runtime.InteropServices;
using System.IO;
using EnvDTE;
using Utils;
using Debugger = System.Diagnostics.Debugger;

namespace Microsoft.CodeOutliner
{
    public partial class OutlineEntry : UserControl
    {
        private XElement element;
        private XElement parentElement;
        private XNamespace _namespace = "http://www.mindchemistry.com/Schemas/CodeOutliner.xsd";
        public event EventHandler OnRemove;
        public event EventHandler OnDirty;
        private CodeOutlinerPackage myPackage;
        public string FileName { get; set; }
        public int LineNumber { get; set; }
        private Dictionary<Control, Control> CachedControls = new Dictionary<Control,Control>();

        public OutlineEntry(XElement element, CodeOutlinerPackage myPackage, bool isNewChild = false)
        {
            this.Load += new EventHandler(Form_Load);

            if (isNewChild)
            {
                var subEntries = element.Element(_namespace + "SubEntries");

                if (subEntries == null)
                {
                    subEntries = new XElement(_namespace + "SubEntries");

                    element.Add(subEntries);
                }

                this.parentElement = element;

                element = new XElement(parentElement.Name);

                subEntries.Add(element);

                element.Add(new XElement(_namespace + "Name"));
                element.Add(new XElement(_namespace + "Description"));

                var codeLocation = new XElement(_namespace + "CodeLocation");

                element.Add(codeLocation);

                codeLocation.Add(new XElement(_namespace + "FileName"));
                codeLocation.Add(new XElement(_namespace + "LineNumber"));

                this.element = element;
            }
            else
            {
                this.element = element;
            }

            this.myPackage = myPackage;

            InitializeComponent();
        }

        private void Form_Load(object sender, EventArgs e)
        {
            if (element != null)
            {
                FillEntryData(element);
            }
        }

        private void FillEntryData(XElement element)
        {
            this.txtName.Text = element.Element(_namespace + "Name").Value;
            this.txtDescription.Text = element.Element(_namespace + "Description").Value;

            var codeLocation = element.Element(_namespace + "CodeLocation");

            if (codeLocation != null)
            {
                this.FileName = codeLocation.Element(_namespace + "FileName").Value;
                int result;

                if (int.TryParse(codeLocation.Element(_namespace + "LineNumber").Value, out result))
                {
                    this.LineNumber = result;
                }

                if (!string.IsNullOrEmpty(this.FileName))
                {
                    var fileInfo = new FileInfo(this.FileName);
                    var dte = myPackage.DTE;
                    var filePath = string.Empty;

                    if (fileInfo.Exists)
                    {
                        if (!string.IsNullOrEmpty(dte.Solution.FullName))
                        {
                            var solutionPath = new FileInfo(dte.Solution.FullName).DirectoryName;

                            if (FileName.StartsWith(solutionPath))
                            {
                                filePath = FileName.RemoveStart(solutionPath);
                            }
                        }
                        else
                        {
                            filePath = FileName;
                        }

                        cmdGoToCode.Text = "Go to " + filePath + " line:" + LineNumber.ToString();
                    }
                    else
                    {
                        cmdGoToCode.Text = "Invalid file name: " + this.FileName;
                        cmdGoToCode.Enabled = false;
                    }
                }
                else
                {
                    cmdGoToCode.Text = "Click 'Edit' to add FileName and Location";
                    cmdGoToCode.Enabled = false;
                }

                cmdGoToCode.AutoSize = true;
            }

            var subEntities = element.Element(_namespace + "SubEntries");

            if (subEntities != null)
            {
                subEntities.Elements().ToList().ForEach(e =>
                {
                    AddEntry(e);
                });
            }
        }

        private OutlineEntry AddEntry(XElement element)
        {
            var entry = new OutlineEntry(element, myPackage);

            this.SubPanel.Controls.Add(entry);

            this.cmdRemove.Visible = true;
            this.cmdAddRow.Visible = false;

            entry.OnDirty += (sender2, e2) =>
            {
                OnDirty(sender2, e2);
            };

            entry.OnRemove += (sender2, e2) =>
            {
                this.cmdAddRow.Visible = true;
                this.SubPanel.Controls.Clear();
            };

            return entry;
        }

        private OutlineEntry AddEntry()
        {
            var entry = new OutlineEntry(element, myPackage, true);

            this.SubPanel.Controls.Add(entry);

            this.cmdRemove.Visible = true;
            this.cmdAddRow.Visible = false;

            entry.OnRemove += (sender2, e2) =>
            {
                this.cmdAddRow.Visible = true;
                this.SubPanel.Controls.Clear();
            };

            entry.OnDirty += (sender2, e2) =>
            {
                OnDirty(sender2, e2);
            };

            return entry;
        }

        private void cmdAddRow_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            AddEntry();
        }

        private void cmdRemove_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            OnRemove(this, EventArgs.Empty);
        }

        private void cmdGoToCode_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            var dte = myPackage.DTE;
            var fileInfo = new FileInfo(this.FileName);

            if (!fileInfo.Exists)
            {
                MessageBox.Show("File does not exist!");
                return;
            }

            //Make it visible, and stay under user control, not close when this app closes

            dte.MainWindow.Visible = true;
            dte.UserControl = true;

            //Open up incident file and select given line

            var window = dte.ItemOperations.OpenFile(fileInfo.FullName, EnvDTE.Constants.vsViewKindTextView);
            var selection = dte.ActiveDocument.Selection as TextSelection;

            selection.GotoLine(LineNumber, true);
        }

        private void txtName_TextChanged(object sender, EventArgs e)
        {
            var name = element.Element(_namespace + "Name");

            name.Value = txtName.Text;

            if (OnDirty != null)
            {
                OnDirty(this, EventArgs.Empty);
            }
        }

        private void txtDescription_TextChanged(object sender, EventArgs e)
        {
            var description = element.Element(_namespace + "Description");

            description.Value = txtDescription.Text;

            if (OnDirty != null)
            {
                OnDirty(this, EventArgs.Empty);
            }
        }

        private void cmdEdit_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            if (cmdEdit.Text == "Edit")
            {
                cmdGoToCode.Visible = false;

                txtFileName.Visible = true;

                txtFileName.Width = LocationPanel.Width - txtLineNumber.Width - 20;
                txtLineNumber.Visible = true;

                txtFileName.Text = this.FileName;
                txtLineNumber.Text = this.LineNumber.ToString();

                cmdEdit.Text = "Save";
            }
            else
            {
                var codeLocation = element.Element(_namespace + "CodeLocation");
                var fileNameElem = codeLocation.Element(_namespace + "FileName");
                var lineNumberElem = codeLocation.Element(_namespace + "LineNumber");
                var filePath = txtFileName.Text;
                var lineNumber = int.Parse(txtLineNumber.Text);
                var fileInfo = new FileInfo(filePath);

                if (fileInfo.Exists)
                {
                    var dte = myPackage.DTE;
                    var solutionPath = new FileInfo(dte.Solution.FullName).DirectoryName;

                    this.FileName = filePath;
                    this.LineNumber = lineNumber;

                    fileNameElem.Value = filePath;
                    lineNumberElem.Value = lineNumber.ToString();

                    if (filePath.StartsWith(solutionPath))
                    {
                        filePath = FileName.RemoveStart(solutionPath);
                    }

                    txtFileName.Visible = false;
                    txtLineNumber.Visible = false;

                    cmdGoToCode.Text = "Go to " + filePath + " line:" + LineNumber.ToString();
                    cmdGoToCode.AutoSize = true;

                    cmdGoToCode.Visible = true;
                    cmdGoToCode.Enabled = true;

                    cmdEdit.Text = "Edit";

                    OnDirty(this, EventArgs.Empty);
                }
                else
                {
                    MessageBox.Show("Invalid file name");
                }
            }
        }

        private void ExpandCollapse_CheckedChanged(object sender, EventArgs e)
        {
            if (ExpandCollapse.Checked)
            {
                var size = new Size(Math.Max(500, SubPanel.Width), txtName.Height + SubPanel.Height + 150);

                this.AutoSize = false;

                LayoutPanel.Controls.Remove(txtDescription);
                LayoutPanel.Controls.Remove(SubPanel);

                LayoutPanel.Controls.Add(SubPanel, 1, 1);

                SubPanel.Dock = DockStyle.Fill;

                this.Size = size;
            }
            else
            {
                this.AutoSize = true;
            }
        }

        private void cmdGoToCode_Enter(object sender, EventArgs e)
        {
            cmdGoToCode.Font = new Font(cmdGoToCode.Font, FontStyle.Bold);
        }

        private void cmdGoToCode_Leave(object sender, EventArgs e)
        {
            cmdGoToCode.Font = new Font(cmdGoToCode.Font, FontStyle.Regular);
        }
    }
}
